home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
DISK
/
S14.ARJ
/
S.ASM
next >
Wrap
Assembly Source File
|
1992-01-05
|
31KB
|
1,216 lines
;TASM source for S.COM v.1.4. Copyright (C) DA Nye, 1992, all rights reserved.
ideal
model tiny
NFILES EQU 200 ;Max # files in display
FILESPEC EQU 82h ;Address of filespec in command tail
PATH EQU 81h ;Count of chars in path part of filespec here
NORMAL_FILE EQU 11100001b ;Attribute for a normal file
TAGGED EQU 80h ;Bit to set in attrib to indicate file tagged
DTA_ATTRIB EQU DTA+21 ;Offsets of elements in DTA
DTA_TIME EQU DTA+22
DTA_DATE EQU DTA+24
DTA_SIZE EQU DTA+26
DTA_NAME EQU DTA+30
FILE_TIME EQU bp ;Addrs of elements in 'fileRecords' after name
FILE_DATE EQU bp+2
FILE_SIZE EQU bp+4
INVERSE_VIDEO EQU 70h ;Black on white
NORMAL_VIDEO EQU 17h ;White on blue
COPYVAL EQU 1 ;Keys for Copy/Move/Delete/Rename operations
DELETEVAL EQU 2
MOVEVAL EQU 3
RENAMEVAL EQU 4
codeseg
BOF:
org 100h
Start:
mov sp, OFFSET stackEnd ;Initialize stack
sub ax, ax
push ax
mov [programSeg], cs ;Save current value of CS -> program segment
mov ah, 0Fh ;Find display memory
int 10h
mov di, 0B000h ;If mode 7 (= MDA or Herc), video seg=B000h,
cmp al, 7
je @@L1
mov di, 0B800h ;Otherwise video seg=B800h,
@@L1:
mov es, di ;Unless running under DesqView
mov cx, 'DE'
mov dx, 'SQ'
mov ax, 2B01h
int 21h
cmp al, 0FFh
je @@L2
mov ah, 0FEh
int 10h
mov di, es
@@L2:
mov [displaySegment], di
mov es, [2Ch] ;Find COMSPEC
sub di, di
@@L3:
mov si, OFFSET comspec
mov cx, 8
repe cmpsb
jne @@L3
mov [comspecOff], di
mov [comspecSeg], es
mov es, [programSeg]
NewSpec:
mov si, FILESPEC ;Examine command line filespec
mov di, si
cmp [byte FILESPEC-2], 1 ;If none, use "*.*"
jbe @@L3
@@L1:
lodsb
cmp al, '*' ;Look for wildcard chars
je GetPathLength
cmp al, '?'
je GetPathLength
cmp al, 0Dh
jne @@L1
cmp [byte si-2], '\' ;If none, assume spec is a directory
jne @@L2
dec si ;If last char was not '\', add it
jmp SHORT @@L3
@@L2:
mov [byte si-1], '\'
@@L3:
mov ax, si ;Compute length of path minus filename
sub ax, di
mov [PATH], al
mov di, si
NewDir:
mov [keepSorted], 0 ;Directory is initially unsorted
mov si, OFFSET starDotStar ;Append '*.*',0
call CopyString
jmp SHORT Restart
NoRoomError:
mov dx, OFFSET noRoomMsg ;Not enough room to run S
Abort:
call BlankScreen
mov ah, 9
int 21h
mov ax, 4C00h ;Bye
int 21h
GetPathLength:
mov al, [byte FILESPEC-2] ;Null-terminate other filespecs
sub ah, ah
mov si, ax
add si, FILESPEC-1
mov [si], ah
mov cx, ax ;Count chars in path excluding file name:
std ;Search backward for last '\' or ':'
@@L1:
lodsb
cmp al, '\'
je @@L2
cmp al, ':'
loopne @@L1
@@L2:
cld
mov [PATH], cl
Restart:
mov ax, OFFSET files ;Cursor -> first file
mov [cursor], ax
mov [top], ax
Restart0:
mov bx, (EOF-BOF)/16+1 ;Resize memory to amount needed
mov ah, 4Ah
int 21h
jc NoRoomError
mov ah, 19h ;Get default drive for path/spec display
int 21h
add al, 'A'
mov di, OFFSET pathNSpec ;Store letter, ':\'
stosb
mov ax, '\:'
stosw
mov si, di ;Get path and name of current directory
sub dl, dl
mov ah, 47h
int 21h
sub al, al ;Find end
mov cx, -1
repne scasb
dec di
mov ax, ' '+100h*' ' ;Add two spaces
stosw
mov si, FILESPEC ;Append filespec
call CopyString
;Get all file names matching filespec and set up tables
GetFileRecords:
mov dx, OFFSET DTA ;Set up DTA
mov ah, 1Ah
int 21h
sub ax, ax ;Initialize file size total
mov [totalSize], ax
mov [totalSize+2], ax
mov dx, FILESPEC ;Get first file name
mov cl, 37h
mov ah, 4Eh
int 21h
jnc FileFound ;No files. Try a different filespec.
mov si, OFFSET NoFilesMsg
call Error
jmp NewFilespec
FileFound:
mov di, OFFSET fileRecords ;DI -> storage for file names
mov bx, OFFSET files ;BX -> array of files
sub bx, 2
StoreFileName:
add bx, 2 ;For all files that will fit,
cmp bx, (OFFSET files) + NFILES*2
jb @@L1
sub bx, 2
mov [last], bx
mov si, OFFSET tooManyMsg
jmp DoError
@@L1:
mov [bx], di ;Store pointer to status/filename in files[]
mov al, [DTA_ATTRIB] ;Store status byte
and al, 3Fh ;Top bit is used to indicate file is marked
stosb
mov si, OFFSET DTA_NAME ;Copy file name from DTA to filename storage
call CopyString
inc di
mov si, OFFSET DTA_TIME ;Copy time, date and size
mov cx, 4
rep movsw
test [DTA_ATTRIB], 10h ;If not a subdirectory,
jnz @@L2
mov si, OFFSET DTA_SIZE ;Add size to total
lodsw
add [totalSize], ax
lodsw
adc [totalSize+2], ax
@@L2:
mov ah, 4Fh ;Next filename
int 21h
jnc StoreFileName
mov [last], bx ;Save pointer to last file entry
mov al, [keepSorted] ;If returning from EXEC, need to resort files?
or al, al
jz DisplayFiles
jmp Sort0
;Main loop. Display files and wait for command.
DisplayFiles:
call BlankStatus ;Clear status line
mov si, OFFSET helpF1 ;Display help key
call DisplayString
mov bx, [cursor]
mov si, [bx]
lodsb ;Get attributes of file at cursor
mov [attrib], al ;Save attribute byte
call DisplayString ;Display name of highlighted file
mov bp, si ;Save pointer to time, date, size
test [attrib], 10h ;If a directory,
jz @@L7
mov si, OFFSET dirMsg ; show '<DIR>' instead of file size
add di, 2
call DisplayString
jmp SHORT @@L9
@@L7:
lea si, [FILE_SIZE] ;File size, right justified
add di, 14
call WriteLongDecimal
@@L9:
add di, 18 ;File date:
push di
sub dx, dx
mov bx, [FILE_DATE] ;Year
mov al, bh
shr al, 1
add al, 80
sub ah, ah
mov cl, 2
call WriteDecimal
mov al, '/'
stosw
mov ax, bx ;Day
and ax, 1Fh
mov cx, 2
call WriteDecimal
mov al, '/'
stosw
mov ax, bx ;Month
mov cl, 5
shr ax, cl
and ax, 0Fh
mov cl, 2
call WriteDecimal
pop di
add di, 12
push di
mov bx, [FILE_TIME] ;File time:
mov ax, bx ;Minutes
mov cl, 5
shr ax, cl
and ax, 3Fh
mov cx, 2
call WriteDecimal
mov al, ':'
stosw
mov al, bh ;Hours
mov cl, 3
shr al, cl
sub ah, ah
sub cl, cl
call WriteDecimal
cld
pop di
add di, 4
mov dl, [attrib] ;Display attribute letters
test dl, 1 ;Read-only
jz @@L3
mov al, 'R'
stosw
@@L3:
test dl, 2 ;Hidden
jz @@L4
mov al, 'H'
stosw
@@L4:
test dl, 4 ;System
jz @@L5
mov al, 'S'
stosw
@@L5:
test dl, 20h ;Archive
jz @@L6
mov al, 'A'
stosw
@@L6:
mov al, 186 ;Display divider
stosw
mov si, OFFSET pathNSpec ;Display path and filespec
call DisplayString
mov di, 158 ;Display total size of displayed files
mov si, OFFSET totalSize
call WriteLongDecimal
mov bx, [top]
mov di, 160
cld
DisplayNext:
mov ah, NORMAL_VIDEO ;Set to inverse video if cursor line
cmp bx, [cursor]
jne @@L0
mov ah, INVERSE_VIDEO
@@L0:
cmp bx, [last] ;If done with files,
jle @@L0a
mov cx, 16 ;Blank out name area
jmp SHORT @@L7
@@L0a:
mov si, [bx] ;Get table entry for a file
lodsb ;Get status byte
test al, TAGGED ;If file has been tagged, display '>'
mov al, '>'
jnz @@L1
mov al, ' '
@@L1:
stosw
mov cx, 9 ;In field of 9,
@@L2:
lodsb ;Display filename up to extension
cmp al, '.'
je @@L3
or al, al
jz @@L6
@@L2a:
stosw
loop @@L2
@@L3:
cmp cx, 9 ;Check for special cases of '.', '..'
je @@L2a
cmp [byte si-2], '.'
je @@L2a
mov al, ' ' ;Else pad with spaces out to 9 chars
rep stosw
@@L4:
mov cx, 6 ;Display extension in field of 6
@@L5:
lodsb
or al, al
jz @@L7
stosw
loop @@L5
jmp SHORT @@L7
@@L6:
add cx, 6 ;Just pad with blanks if no extension
@@L7:
mov al, ' '
rep stosw
cmp di, 4000 ;Stop at screenful
je GetCommand
cmp di, 3872
jb @@L8
sub di, 3808 ;Next column
@@L8:
add di, 128 ;Next row
add bx, 2
jmp DisplayNext
;Get command
GetCommand:
call HideCursor
mov es, [programSeg]
mov ah, 8 ;Get keypress
sub ch, ch
@@L1:
inc ch
int 21h
or al, al
jz @@L1
mov ah, ch ;AH = 2 if aux code, 1 if plain ASCII
call ToUpper
mov di, OFFSET CommandKeys ;Look it up, get pointer to routine
mov cx, NCOMMANDS
repne scasw
jne InvalidCommand
add di, CommandAddrs-CommandKeys-2
mov bx, [cursor] ;SI -> file record for highlighted file
mov si, [bx]
call ShowCursor
jmp [word di] ;Jump to routine
InvalidCommand:
call Beep
jmp GetCommand
;********************************* Commands ***********************************
Up:
sub bx, 2
jmp SHORT NewLine
Down:
add bx, 2
jmp SHORT NewLine
Left:
sub bx, 48
jmp SHORT NewLine
Right:
add bx, 48
jmp SHORT NewLine
PageUp:
sub bx, 238
jmp SHORT NewLine
PageDown:
add bx, 238
NewLine:
cmp bx, [last] ;Make sure cursor is still within bounds
jbe @@L1
mov bx, [last]
@@L1:
cmp bx, OFFSET files
jae @@L2
mov bx, OFFSET files
@@L2:
cmp bx, [top] ;Slide window if off screen
jae @@L3
mov [top], bx
jmp SHORT @@L4
@@L3:
mov ax, bx
sub ax, 238
cmp ax, [top]
jb @@L4
mov [top], ax
@@L4:
mov [cursor], bx
jmp DisplayFiles
Tag:
test [byte si], NOT NORMAL_FILE ;Only allow marking of normal files
jnz @@L1
xor [byte si], TAGGED
@@L1:
jmp DisplayFiles
Go:
mov [byte inputString], 0 ;Initialize to no user-entered command tail
Go0:
lodsb ;Get attribute byte
mov dx, si ;Join path and file name
mov si, PATH
mov di, OFFSET buffer
call Join
test al, 10h ;If a directory,
jz DoExec
ChangeDir:
mov dx, OFFSET buffer
mov ah, 3Bh ;Change to it
int 21h
mov [byte PATH], 0 ;No path now
mov di, FILESPEC ;Read in contents of new directory
jmp NewDir
DoExec:
mov si, dx ;Find extension
@@L1:
lodsb
cmp al, '.'
je @@L2
or al, al
jne @@L1
jmp SHORT @@L4
@@L2:
mov di, OFFSET extensions ;If .EXE, .COM or .BAT, execute it
mov dx, si
@@L3:
mov si, dx
mov cx, 3
repe cmpsb
je @@L5
sub al, al
repne scasb
cmp [byte di], 0
jne @@L3
@@L4:
mov si, OFFSET notExecMsg ;Else error, not an executable file
jmp DoError
@@L5:
call BlankScreen
mov dx, OFFSET buffer ;If .BAT, need COMMAND.COM, otherwise don't
cmp [byte di], 0
jne @@L6
mov si, OFFSET CC
label DoEdit near ;Edit function enters here
mov di, OFFSET EXECCmdLine+1
call CopyString ;Store '/c ' or '/c <editor> ' to command tail
mov dl, cl
mov si, OFFSET buffer ;Append path\file
call CopyString
add dl, cl
mov [EXECCmdLine], dl ;Store length, append CR
mov [byte di], 13
mov dx, [comspecOff]
mov ds, [comspecSeg]
@@L6:
cmp [byte cs:inputString], 0 ;If a command tail was entered,
jz DoDOS
push ds
mov ds, [cs:programSeg]
mov di, OFFSET EXECCmdLine+1
mov al, ' ' ;Add a space
stosb
mov si, OFFSET inputString ;Add command tail
call CopyString
inc cl
add [EXECCmdLine], cl
mov [byte di], 13
pop ds
DoDOS:
mov cx, bx
mov bx, (inputString-BOF)/16+1 ;Release unneeded memory
mov ah, 4Ah
int 21h
jc SysErr
call BlankScreen
push cx
mov [cs:temp], sp ;Do EXEC
mov bx, OFFSET EXECParams
mov ax, 4B00h
int 21h
mov bx, cs ;Restore critical registers
mov ds, bx
mov es, bx
mov ss, bx
mov sp, [temp]
pop bx
jnc @@L2
mov si, OFFSET sysErrMsg ;EXEC error
cmp al, 8 ;If return code = 8, no room
jne @@L1
mov si, OFFSET noExecRoomMsg
@@L1:
jmp DoError
@@L2:
mov [byte EXECCmdLine], 0 ;Tidy up
jmp Restart0
SysErr:
mov si, OFFSET sysErrMsg
DoError:
call Error
jmp DisplayFiles
GoCL:
mov si, OFFSET commandTailMsg ;Prompt for command tail
call Query
mov si, [bx]
jmp Go0
DOS:
call BlankScreen
mov dx, [comspecOff]
mov ds, [comspecSeg]
jmp DoDOS
Edit:
lea dx, [si+1] ;Join path and file name
mov si, PATH
mov di, OFFSET buffer
call Join
mov si, OFFSET editor ;Invoke editor
mov [byte inputString],0
jmp DoEdit
Copy:
mov [byte CMDR], COPYVAL ;Set Copy/Move/Delete/Remove key to Copy
jmp SHORT DoCopy
Delete:
mov [byte CMDR], DELETEVAL ;Set Copy/Move/Delete/Remove key to Delete
jmp SHORT DoCMDR
Move:
mov [byte CMDR], MOVEVAL ;Set Copy/Move/Delete/Rename key to Move
DoCopy:
mov si, OFFSET DestMsg ;If Copy or Move, prompt for destination
call Query
mov di, si
mov [byte si], '\' ;Append '\'
inc si
mov [temp], si
cmp [byte CMDR], MOVEVAL
jne DoCMDR
mov ax, [word FILESPEC] ;If Move to same drive,
mov dx, [word inputString]
cmp ah, ':'
je @@L1
cmp dh, ':'
jne @@L2
@@L1:
cmp ax, dx
jne DoCMDR
@@L2:
mov [byte CMDR], RENAMEVAL ; do Rename instead (much faster)
DoCMDR:
mov bp, OFFSET files - 2 ;For each file
CMDRNext:
add bp, 2
cmp bp, [last]
jbe @@L0
jmp GetFileRecords
@@L0:
mov si, [bp] ;Skip if not tagged
lodsb
test al, TAGGED
jz CMDRNext
xor [byte si-1], TAGGED ;Else untag
mov dx, si
mov si, PATH ;Source path\filename -> sourceFileSpec
mov di, OFFSET sourceFileSpec
call Join
cmp [byte CMDR], DELETEVAL ;If not Deleting
je @@L4
mov si, dx ;Append current file's name to destination path
mov di, [temp]
call CopyString
cmp [byte CMDR], RENAMEVAL ;If Rename, do it
jne @@L2
label DoRename near
mov dx, OFFSET sourceFileSpec
mov di, OFFSET inputString
mov ah, 56h
int 21h
jnc @@L1
mov dx, di ;If rename failed, try deleting target name
mov ah, 41h
int 21h
jnc DoRename ; and try again
jmp SHORT CantOpen ;If delete failed, abort
@@L1:
jmp CMDRNext
@@L2:
mov dx, OFFSET sourceFileSpec ;Copy or Move: open source, dest files
mov ax, 3D00h
int 21h
jc CantOpen
mov [sourceHandle], ax
sub cx, cx
mov dx, OFFSET inputString
mov ax, 3C00h
int 21h
jc CantOpen
mov [destHandle], ax
@@L3:
mov bx, [sourceHandle] ;Read a bufferful
mov cx, 512
mov dx, OFFSET buffer
mov ah, 3Fh
int 21h
jc ReadError
mov bx, [destHandle] ;Write it
mov cx, ax
mov ah, 40h
int 21h
jc WriteError
cmp ax, cx
jb FullError
cmp cx, 512 ;Loop until done
je @@L3
mov ah, 3Eh ;Close files
mov bx, [sourceHandle]
int 21h
mov bx, [destHandle]
int 21h
cmp [CMDR], MOVEVAL ;If Move, now do Delete
jne @@L1
@@L4:
mov bx, OFFSET sourceFileSpec ;Delete file
mov ah, 41h
int 21h
jnc @@L1
CantOpen:
mov si, OFFSET cantOpenMsg
jmp DoError
ReadError:
mov si, OFFSET readMsg
jmp DoError
WriteError:
mov si, OFFSET writeMsg
jmp DoError
FullError:
mov si, OFFSET fullMsg
jmp DoError
NewFilespec:
mov si, OFFSET newSpecMsg ;Prompt for new filespec
call Query
inc al
mov [FILESPEC-2], al ;Store count of chars
mov si, OFFSET inputString
mov di, FILESPEC
call CopyString ;Copy new filespec to command tail area
mov [byte di], 0Dh ;Append CR
jmp NewSpec ;Process new filespec
Rename:
lea dx, [si + 1] ;Join path and current name
mov si, PATH
mov di, OFFSET sourceFileSpec
call Join
mov si, OFFSET newNameMsg ;Prompt for new name of file or directory
call Query
jmp DoRename
Drive:
mov si, OFFSET newDriveMsg ;Prompt for letter of drive to change to
call QueryChar
sub al, 'A'
mov dl, al
mov ah, 0Eh
int 21h
cmp dl, al ;Ask again if that drive doesn't exist
jb @@L1
mov si, OFFSET badDriveMsg
jmp DoError
@@L1:
mov [byte FILESPEC-2], 0 ;If successful, start with default filespec
jmp NewSpec
Sort:
;
;Sort algorithm:
; 1) make up array of records {pointer to field to sort | tag}, one for each
; file, in 'buffer'.
; 2) bubble-sort these records
; 3) copy file record pointers in 'files' to 'buffer' in order of sorted tags
; 4) copy file record pointers back to 'files' in new order
;
mov si, OFFSET sortMsg ;Prompt for sort field
call QueryChar
cmp al, 'N' ;Check for legal sort field option
je Sort0
cmp al, 'E'
je Sort0
cmp al, 'D'
je Sort0
mov si, OFFSET genErrorMsg
jmp DoError
Sort0:
mov [keepSorted], al ;Remember for later resorting after EXEC
mov dl, al
sub dh, dh ;DH = tag (position of file in current order)
mov di, OFFSET buffer
mov bx, OFFSET files
@@L1:
mov si, [bx] ;Find field to sort: get pointer to record
@@L1a:
inc si
mov cx, si
cmp dl, 'N' ;If Name, already pointing at it
je @@L4
sub ah, ah ;If Date, find null at end of name
cmp dl, 'E' ;If Ext find '.' or end of name
jne @@L2
cmp [byte si], '.' ;'.' and '..' are special cases
je @@L1a
mov ah, '.'
@@L2:
lodsb
or al, al
je @@L3
cmp al, ah
jne @@L2
@@L3:
dec si ;If Ext, back up to '.' or null
cmp dl, 'E'
je @@L4
add si, 3 ;If Date, advance to date field
@@L4:
mov ax, si ;Store pointer to field to sort
stosw
mov al, dh ;Store tag
stosb
inc dh ;Bump tag
add bx, 2 ;Loop until no more files
cmp bx, [last]
jbe @@L1
DoSort:
lea bp, [di-3] ;BP -> last
push bp
@@L0:
mov bx, OFFSET buffer ;Do bubble sort
@@L1:
mov si, [bx]
cmp [word si-1], '.' ;Leave '.' and '..' alone
je @@L4
mov di, [bx+3]
cmp dl, 'D' ;If sorting Dates, compare one word
jne @@L2
cmpsw
jmp SHORT @@L3
@@L2:
mov cx, -1 ;Else compare bytes until not equal
repe cmpsb
@@L3:
jbe @@L4 ;If first field > second
mov ax, [bx] ;Exchange field pointers and tags
xchg ax, [bx+3]
mov [bx], ax
mov al, [bx+2]
xchg al, [bx+5]
mov [bx+2], al
@@L4:
add bx, 3 ;Loop until no more files this pass
cmp bx, bp
jb @@L1
sub bp, 3
cmp bp, OFFSET buffer ;Loop until no more passes
jne @@L0
OrderByTags:
mov di, OFFSET buffer ;Arrange file pointers in order of tags
mov si, OFFSET files
pop bp
@@L1:
mov bl, [di+2] ;Get tag
sub bh, bh
add bx, bx
mov ax, [bx+si] ;Get file ptr associated with that tag
stosw ;Store in place of field pointer in sort buffer
inc di
cmp di, bp
jbe @@L1
mov si, OFFSET buffer
mov di, OFFSET files
@@L2:
movsw ;Copy file pointers back in new order
inc si
cmp di, [last]
jbe @@L2
jmp DisplayFiles
Help:
call BlankStatus ;Display help on status line
mov si, OFFSET helpMsg
call DisplayString
mov ah, 8
int 21h
jmp DisplayFiles
Exit:
call BlankScreen
mov ax, 4C00h ;Bye
int 21h
;******************************* Subroutines **********************************
WriteLongDecimal:
;
;Display 4-byte integer
; IN: SI -> number, DI -> display address of least significant digit
; OUT: none
;USED: AX CX DX
push cx
push dx
push di
mov ax, [si]
mov dx, [si + 2]
mov cx, 10000 ;Divide by 10000, show quotient|remainder
div cx
xchg ax, dx
or dx, dx
jz @@L1
mov cl, 4
call WriteDecimal
mov ax, dx
@@L1:
sub cl, cl
call WriteDecimal
pop di
pop dx
pop cx
ret
WriteDecimal:
;
;Display a decimal number in inverse video, writing digits backwards from right.
; IN: AX = number, CL = field width with leading 0s (no leading 0s if CL = 0)
; ES:DI -> Video RAM where rightmost digit will go
; OUT: AH = INVERSE_VIDEO, DI -> left of first digit
;USED: AL, CX
push bx
push dx
std
mov bx, 10
sub ch, ch
@@L1:
sub dx, dx ;Get a digit
div bx
xchg ax, dx ;Write it to display, right to left
add ax, '0' + INVERSE_VIDEO*100h
stosw
xchg ax, dx
cmp cx, 0 ;If CX > 0, loop even if AX = 0
jg @@L2
or ax, ax ;Else loop only if AX > 0 (more digits left)
jz @@L3
@@L2:
loop @@L1
@@L3:
pop dx
pop bx
mov ah, INVERSE_VIDEO
Ret1:
ret
DisplayString:
;
;Display string on status line.
; IN: SI -> string (null-terminated), ES:DI -> status line, AH = attribute
; OUT: DI is advanced past end of string
;USES: AL SI
@@L1:
lodsb
or al, al
jz Ret1
stosw
jmp @@L1
DisplayInputString:
;
;Same as above but show cursor at end of line
; IN, OUT, USES -- see above
push bx
push dx
call DisplayString
mov dx, di ;Set cursor to end of printed string
add dx, 2
shr dl, 1
sub dh, dh
mov ah, 2
sub bx, bx
int 10h
pop dx
pop bx
ret
Error:
;
;Beep, display string on status line and wait for keypress (any key)
; IN: SI -> string
; OUT: ES -> program segment
;USED: AX
call Beep
call BlankStatus
call DisplayString ;Display error string
mov si, OFFSET ErrorMsg ;Display 'Press any key' message
call DisplayString
mov ah, 8
int 21h
mov es, [programSeg]
ret
Query:
;
;Prompt for string input on status line
; IN: SI -> message
; OUT: SI -> null at end of ASCIIZ string input, AX = length (excluding null)
;USED: none
push bx
push cx
push dx
push di
push es
call BlankStatus
call DisplayInputString
mov cx, 80 ;Get input
mov dx, OFFSET inputString
mov si, dx
sub bx, bx
mov ah, 3Fh
int 21h
sub ax, 2
add si, ax
mov [byte si], 0 ;Null-terminate it
pop es
pop di
pop dx
pop cx
pop bx
ret
QueryChar:
;
;Prompt for single character input
; IN: SI -> message
; OUT: AL = character (lower case converted to upper)
;USED: AH
push es
call BlankStatus
call DisplayInputString ;Display string
mov ah, 8 ;Get char
int 21h
call ToUpper
pop es
ret
BlankStatus:
;
;Clear top line of display to inverse video
; IN: none
; OUT: ES = video segment, DI = 0
;USED: AX CX
mov ax, [displaySegment]
mov es, ax
sub di, di
mov ax, ' ' + INVERSE_VIDEO*100h
mov cx, 80
rep stosw
sub di, di
ret
Join:
;
;Copy counted path, then ASCIIZ file name to buffer and null-terminate
; IN: SI -> path, DX -> file, DI -> destination
; OUT: DI -> null at end of copied string, CX = total chars excluding null
;USED: none
push ax
push si
lodsb ;Get count of chars in path (a counted string)
sub ah, ah
mov cx, ax
rep movsb ;Copy it to destination
mov cx, ax
mov si, dx ;Now copy file name (null-terminated)
call CopyString
add cx, ax ;Sum string counts -> CX
mov [byte di], 0 ;Null-terminate the result
pop si
pop ax
ret
CopyString:
;
;Copy null-terminated string.
; IN: SI -> string, DI -> destination
; OUT: CX = length (excluding null), DI -> terminating null of copied string
;USED: AL
mov cx, -1
@@L1:
lodsb ;Copy string
stosb
or al, al ;Until null at end is encountered
loopnz @@L1 ;Accumulate count of chars
neg cx ;Adjust count
sub cx, 2
dec di ;DI -> terminating null
ret
HideCursor:
;
;Move cursor off bottom of screen
; IN: none
; OUT: none
;USED: AH
push bx
push dx
mov dx, 1900h
DoCursor:
sub bh, bh
mov ah, 2
int 10h
pop dx
pop bx
ret
ShowCursor:
;
;Put cursor back at 0,0
; IN: none
; OUT: none
;USED: AH
push bx
push dx
sub dx, dx
jmp DoCursor
Beep:
;
;Output a bell char
; IN: none
; OUT: none
;USED: AH DL
mov dl, 7
mov ah, 2
int 21h
ret
BlankScreen:
;
;Clear screen and home cursor
; IN: none
; OUT: none
;USED: AX
push cx
push es
push di
mov es, [cs:displaySegment] ;Blank screen
sub di, di
mov ax, ' '+NORMAL_VIDEO*100h
mov cx, 25 * 80
rep stosw
pop di
pop es
pop cx
ret
ToUpper:
;
;Convert lower to upper case
; IN: AL = char
; OUT: AL = char
;USED: none
cmp al, 'a'
jb @@L1
cmp al, 'z'
ja @@L1
add al, 'A'-'a'
@@L1:
ret
;********************************** Data **************************************
;Command dispatch table: aux,2 or ASCII,1 paired to command routine addresses
commandKeys db 72,2, 80,2, 75,2, 77,2, 73,2, 81,2, 'T',1, 13,1, 'C',1
db 'D',1, 'M',1, 'R',1, 'E',1, 'F',1, 'V',1, 'S',1
db 60,2, 10,1, 59,2, 27,1
commandAddrs dw Up, Down, Left, Right, PageUp, PageDown, Tag, Go, Copy
dw Delete, Move, Rename, Edit, NewFilespec, Drive, Sort
dw DOS, GoCL, Help, Exit
NCOMMANDS EQU (commandAddrs-commandKeys)/2
;Strings
helpMsg db 'Copy Delete Edit Filespec Move Ren Sort '
db 'Tag driVe Enter=cd/run F2=DOS Esc=exit',0
helpF1 db 'Help F1',186,0
noFilesMsg db 'No matching files or invalid path',0
noRoomMsg db 'Out of room$'
badPathMsg db 'Bad path',0
sysErrMsg db 'System error',0
cantOpenMsg db "Can't open file",0
writeMsg db 'Write error',0
fullMsg db 'Disk full',0
readMsg db 'Read error',0
notExecMsg db 'Not a directory or executable file',0
badDriveMsg db "Drive doesn't exist",0
genErrorMsg db 'Error',0
tooManyMsg db 'Too many files',0
noExecRoomMsg db 'Not enough memory',0
ErrorMsg db '. Press any key.',0
destMsg db 'Where to?',0
newSpecMsg db 'New filespec:',0
newDriveMsg db 'New drive:',0
sortMsg db 'Sort on: Name Ext Date',0
newNameMsg db 'New name:',0
commandTailMsg db 'Command tail:',0
dirMsg db '<DIR>',0
extensions db 'EXECOMBAT',0
starDotStar db '*.*',0
editor db '/C E ', 12 dup (0)
cc db '/C ',0
comspec db 'COMSPEC='
;EXEC function parameter block
EXECParams dw 0
EXECCmdLineOff dw OFFSET EXECCmdLine
programSeg dw 0
dw -1, -1 , -1 , -1
EXECCmdLine db 0, 80 dup (?)
sourceFileSpec EQU EXECCmdLine ;Second use for this space during Copy etc.
;Variables, buffers
dw 128 dup (?) ;Stack (here for protection during EXEC)
stackEnd:
keepSorted db ? ;Holds sort subcommand char if this dir sorted
CMDR db ? ;Key for Copy/Move/Delete/Rename actions
attrib db ? ;File attribute byte
totalSize dw ?, ? ;Sum of file sizes
sourceHandle dw ? ;Source handle for Copy, etc.
destHandle dw ? ;Destination handle for Copy etc.
cursor dw ? ;Position in 'files' of highlighted file
last dw ? ;Position in 'files' of last file in directory
top dw ? ;Position in 'files' of file at top of screen
temp dw ? ;Holds SP during EXEC, other uses
comspecSeg dw ? ;Segment of environment
comspecOff dw ? ;Offset of 'C:\COMMAND.COM' in environment
displaySegment dw ? ;Segment of display RAM
pathNSpec db 80 dup (?) ;Default path and current filespec for display
DTA db 64 dup (?) ;Disk Transfer Area
inputString db 80 dup (?) ;String returned by 'Query', other uses
files dw NFILES dup (?) ;Array of pointers to file records
fileRecords db 80*NFILES dup (?) ;File records: attrib/name/time/date
buffer db 512 dup (?) ;Buffer for Copy, etc.
EOF:
end Start